安全管理(zero-rbac)
白皮书中的权限管理主要包括:
-
全局配置:zero-rbac常用模块配置以及模块中所支持的功能。
-
消费端:XBAC模型:基于角色的RBAC(Role)、基于属性的ABAC(Attribute)、基于策略的PBAC(Policy)。
-
认证/授权流程:再谈Zero中的
@Wall、详解 401/403 流程。 -
管理端:可扩展面板式架构,现阶段版本支持 菜单管理、流程定制 两类,您可以扩展自己所需的不同权限管理的结构。
|
Zero在线教程中讲解过可插拔模式的认证组件,此处略过认证部分( |
若想要启用Zero Extension中的安全管理,可在您的Maven中追加如下依赖:
<!-- RBAC专用模块 -->
<dependency>
<groupId>cn.vertxup</groupId>
<artifactId>zero-rbac</artifactId>
<version>ZERO_VERSION</version> <!-- 版本跟随Zero版本 -->
</dependency>
全局配置
模块配置
zero-rbac 模块的基本模块配置位于文件 plugin/rbac/configuration.json 中,下边是一个完整的范例
{
"condition": {
"user": [
"sigma"
],
"role": [
"sigma"
],
"group": [
"sigma"
],
"permission": [
"sigma"
],
"resource": [
"sigma"
],
"action": [
"sigma"
]
},
"codeExpired": 30,
"codeLength": 6,
"tokenExpired": 30,
"poolCode": "POOL_CODE",
"poolToken": "POOL_TOKEN",
"poolPermission": "POOL_PERMISSIONS",
"poolResource": "POOL_RESOURCES",
"supportMultiApp": true,
"supportIntegration": false,
"supportGroup": true,
"supportSecondary": true,
"verifyCode": true,
"verifyLimitation": 3,
"category": {
"employee": {
"classDao": "cn.vertxup.erp.domain.tables.daos.EEmployeeDao",
"condition": {
"workNumber,!n": ""
},
"mapping": {
"modelKey": "employeeId"
}
}
},
"initializePassword": "B59C67BF196A4758191E42F76670CEBA",
"initialize": {
"scope": "zero.app.demo",
"grantType": "authorization_code"
}
}
上述配置是一个完整的DEMO,除了初始化密码部分为了脱敏修改过,其他内容都是可参考的:
| 属性 | 含义 |
|---|---|
condition |
默认选择对应实体的查询条件,多应用模式下可直接支持。 |
codeExpired |
临时授权码的时间长度,默认 30s。 |
codeLength |
临时授权码的字符串长度,默认 6位。 |
tokenExpired |
令牌过期时间,默认 30分钟(由于目前版本使用了持久化令牌,该功能还未完善)。 |
poolCode |
临时授权码的缓存池名称。 |
poolToken |
令牌缓存池名称。 |
poolPermission |
权限缓存池名称。 |
poolResource |
资源缓存池名称。 |
supportMultiApp |
是否支持多应用模式。 |
supportIntegration |
是否支持和第三方应用的安全集成。 |
supportGroup |
是否支持用户组功能。 |
supportSecondary |
是否支持权限的二级缓存功能。 |
verifyCode |
是否支持验证码。 |
verifyLimitation |
设置用户登录限制,密码错误超过该值账户会被默认锁定。 |
initializePassword |
新建账号时的初始化密码(一般是导入过程中使用)。 |
initialize |
初始化账号时所需的基本账号信息。 |
category |
打开异构账号模式。 |
|
现阶段版本中 |
新版本之后您可以只使用如下配置:
{
"supportGroup": true,
"supportSecondary": true,
"verifyCode": true,
"verifyLimitation": 3,
"category": {
"employee": {
"classDao": "cn.vertxup.erp.domain.tables.daos.EEmployeeDao",
"condition": {
"workNumber,!n": ""
},
"mapping": {
"modelKey": "key"
}
}
},
"initializePassword": "B59C67BF196A4758191E42F76670CEBA",
"initialize": {
"scope": "zero.app.demo",
"grantType": "authorization_code"
}
}
关于异构账号模式
Zero权限框架中的 S_USER 有两个特殊字段:
| 属性 | 字段 | 含义 |
|---|---|---|
modelKey |
|
账号关联模型类型。 |
modelId |
|
账号关联上述模型中的主键(ID)。 |
从配置 category 中可以了解和账号连接的相关配置,示例配置中的值对应:
-
modelKey = employee
-
modelId = key(员工表
E_EMPLOYEE中的主键)
消费端
XBAC模型
Zero中的基本权限主体模型如下:
上图中的核心概念如下:
| 实体表 | 概念 | 含义 |
|---|---|---|
S_USER |
用户 |
登录系统专用账号信息表。 |
S_ROLE |
角色 |
和账号关联的角色信息表。 |
S_GROUP |
用户组 |
和账号关联的用户组信息表。 |
S_PERMISSION |
权限 |
和角色相关联的权限记录表。 |
S_PERM_SET |
权限集 |
服务于权限定义的权限集合表,主要用于管理端构造权限集合实现批量授权。 |
S_ACTION |
操作 |
隶属于权限记录的操作集合,操作最终会绑定到对应的操作主体上。 |
常用的基本配置中,只要资源所需操作级别和基础算法模型计算的最终权限路径和用户所拥有的权限路径是通的,那么就可以实现基础权限的认证,这部分内容在完成本章节所有讲解之后会有更深入的说明。 |
安全操作 S_ACTION 对端关联到资源表 S_RESOURCE 和资源形成1对1的绑定,资源中定义的就是访问资源的基本要求,而S_ACTION中计算出来的结果就是登录账号所拥有的资源访问资格,当资源访问资格满足了资源基本要求时就认证通过,证明当前账号有权限访问该资源。但是、但是、但是——此处只是解决了账号:能不能 的问题,在资源访问之后还会有两张专用的数据表来执行 访问多少 的问题,这是Zero中数据域的实现原理,在后续的 N维安全视图 中加以说明。
多态身份(Profile)
Zero中由于复杂的多对多结构,最终会形成不同的多态身份(Profile),S_RESOURCE 表中有如下字段对资源访问资格执行定义:
| 字段名 | 含义 | 取值 |
|---|---|---|
MODE_ROLE |
按角色查找资源的模式 |
UNION、EAGER、LAZY、INTERSECT |
MODE_GROUP |
按用户组查找资源的模式 |
HORIZON、CRITICAL、OVERLOOK |
MODE_TREE |
用户组继承和非继承树模式查找 |
EXTEND、PARENT、CHILD、INHERIT |
上述取值是资源对多态身份的定义,最终形成的多态身份 Profile 的值列表如下(举例定义角色和组的优先级):
|
不含组模式
假设用户包含如下信息,这种模式(高频使用模式)下 MODE_GROUP / MODE_TREE 都设置成 NULL:
R1(H):P1、P2、P3,高优先级角色,包含三条权限记录。
R2(L):P2、P4,低优先级角色,包含两条权限记录。
| 值 | 含义 |
|---|---|
USER_UNION |
|
USER_EAGER |
|
USER_LAZY |
|
USER_INTERSECT |
|
用户组模式
假设用户包含如下信息:
用户组结构如下(此处不列举组所对应的权限集):
G10
/ \
G20 G21
/ \ \
G30 G31 G32
而登录用户只包含如下三个用户组:
G20(H):高优先级用户组
G31(M):中优先级用户组
G32(L):低优先级用户组
全量优先级:
G10 > G20 > G21 > G30 > G31 > G32
| 值 | 组计算 | 角色计算 | 计算流程 |
|---|---|---|---|
HORIZON_UNION |
UNION模式计算单个用户组关联角色权限集,再将三个组的权限集合并。 |
||
HORIZON_EAGER |
EAGER模式计算单个用户组关联角色权限集,再将三个组的权限集合并。 |
||
HORIZON_LAZY |
LAZY模式计算单个用户组关联角色权限集,再将三个组的权限集合并。 |
||
HORIZON_INTERSECT |
INTERSECT模式计算单个用户组关联角色权限集,再将三个组的权限集合并。 |
||
CRITICAL_UNION |
UNION模式计算高优先级组的权限集。 |
||
CRITICAL_EAGER |
EAGER模式计算高优先级组的权限集。 |
||
CRITICAL_LAZY |
LAZY模式计算高优先级组的权限集。 |
||
CRITICAL_INTERSECT |
INTERSECT模式计算高优先级的权限集。 |
||
OVERLOOK_UNION |
UNION模式计算低优先级组的权限集。 |
||
OVERLOOK_EAGER |
EAGER模式计算低优先级组的权限集。 |
||
OVERLOOK_LAZY |
LAZY模式计算低优先级组的权限集。 |
||
OVERLOOK_INTERSECT |
INTERSECT模式计算低优先级组的权限集。 |
||
PARENT_HORIZON_UNION |
先查找三个组的父组,再按UNION模式计算权限集。 |
||
PARENT_HORIZON_EAGER |
先查找三个组的父组,再按EAGER模式计算权限集。 |
||
PARENT_HORIZON_LAZY |
先查找三个组的父组,再按LAZY模式计算权限集。 |
||
PARENT_HORIZON_INTERSECT |
先查找三个组的父组,再按INTERSECT模式计算权限集。 |
||
PARENT_CRITICAL_UNION |
先查找优先级最高组的父组,再按UNION模式计算权限集。 |
||
PARENT_CRITICAL_EAGER |
先查找优先级最高组的父组,再按EAGER模式计算权限集。 |
||
PARENT_CRITICAL_LAZY |
先查找优先级最高组的父组,再按LAZY模式计算权限集。 |
||
PARENT_CRITICAL_INTERSECT |
先查找优先级最高组的父组,再按INTERSECT模式计算权限集。 |
||
PARENT_OVERLOOK_UNION |
先查找优先级最低组的父组,再按UNION模式计算权限集。 |
||
PARENT_OVERLOOK_EAGER |
先查找优先级最低组的父组,再按EAGER模式计算权限集。 |
||
PARENT_OVERLOOK_LAZY |
先查找优先级最低组的父组,再按LAZY模式计算权限集。 |
||
PARENT_OVERLOOK_INTERSECT |
先查找优先级最低组的父组,再按INTERSECT模式计算权限集。 |
||
CHILD_HORIZON_UNION |
先查找所有组的子组,再按UNION模式计算权限集。 |
||
CHILD_HORIZON_EAGER |
先查找所有组的子组,再按EAGER模式计算权限集。 |
||
CHILD_HORIZON_LAZY |
先查找所有组的子组,再按LAZY模式计算权限集。 |
||
CHILD_HORIZON_INTERSECT |
先查找所有组的子组,再按INTERSECT模式计算权限集。 |
||
CHILD_CRITICAL_UNION |
先查找优先级最高组的子组,再按UNION模式计算权限集。 |
||
CHILD_CRITICAL_EAGER |
先查找优先级最高组的子组,再按EAGER模式计算权限集。 |
||
CHILD_CRITICAL_LAZY |
先查找优先级最高组的子组,再按LAZY模式计算权限集。 |
||
CHILD_CRITICAL_INTERSECT |
先查找优先级最高组的子组,再按INTERSECT模式计算权限集。 |
||
CHILD_OVERLOOK_UNION |
(无权限)先查找优先级最低组的子组,再按UNION模式计算权限集。 |
||
CHILD_OVERLOOK_EAGER |
(无权限)先查找优先级最低组的子组,再按EAGER模式计算权限集。 |
||
CHILD_OVERLOOK_LAZY |
(无权限)先查找优先级最低组的子组,再按LAZY模式计算权限集。 |
||
CHILD_OVERLOOK_INTERSECT |
(无权限)先查找优先级最低组的子组,再按INTERSECT模式计算权限集。 |
||
INHERIT_HORIZON_UNION |
先查找所有组父组包含本组,再按UNION模式计算权限集。 |
||
INHERIT_HORIZON_EAGER |
先查找所有组父组包含本组,再按EAGER模式计算权限集。 |
||
INHERIT_HORIZON_LAZY |
先查找所有组父组包含本组,再按LAZY模式计算权限集。 |
||
INHERIT_HORIZON_INTERSECT |
先查找所有组父组包含本组,再按INTERSECT模式计算权限集。 |
||
INHERIT_CRITICAL_UNION |
先查找优先级高组的父组包含本组,再按UNION模式计算权限集。 |
||
INHERIT_CRITICAL_EAGER |
先查找优先级高组的父组包含本组,再按EAGER模式计算权限集。 |
||
INHERIT_CRITICAL_LAZY |
先查找优先级高组的父组包含本组,再按LAZY模式计算权限集。 |
||
INHERIT_CRITICAL_INTERSECT |
先查找优先级高组的父组包含本组,再按INTERSECT模式计算权限集。 |
||
INHERIT_OVERLOOK_UNION |
先查找优先级低组的父组包含本组,再按UNION模式计算权限集。 |
||
INHERIT_OVERLOOK_EAGER |
先查找优先级低组的父组包含本组,再按EAGER模式计算权限集。 |
||
INHERIT_OVERLOOK_LAZY |
先查找优先级低组的父组包含本组,再按LAZY模式计算权限集。 |
||
INHERIT_OVERLOOK_INTERSECT |
先查找优先级低组的父组包含本组,再按INTERSECT模式计算权限集。 |
||
EXTEND_HORIZON_UNION |
先查找所有组子组包含本组,再按UNION模式计算权限集。 |
||
EXTEND_HORIZON_EAGER |
先查找所有组子组包含本组,再按EAGER模式计算权限集。 |
||
EXTEND_HORIZON_LAZY |
先查找所有组子组包含本组,再按LAZY模式计算权限集。 |
||
EXTEND_HORIZON_INTERSECT |
先查找所有组子组包含本组,再按INTERSECT模式计算权限集。 |
||
EXTEND_CRITICAL_UNION |
先查找优先级高组的子组包含本组,再按UNION模式计算权限集。 |
||
EXTEND_CRITICAL_EAGER |
先查找优先级高组的子组包含本组,再按EAGER模式计算权限集。 |
||
EXTEND_CRITICAL_LAZY |
先查找优先级高组的子组包含本组,再按LAZY模式计算权限集。 |
||
EXTEND_CRITICAL_INTERSECT |
先查找优先级高组的子组包含本组,再按INTERSECT模式计算权限集。 |
||
EXTEND_OVERLOOK_UNION |
先查找优先级低组的子组包含本组,再按UNION模式计算权限集。 |
||
EXTEND_OVERLOOK_EAGER |
先查找优先级低组的子组包含本组,再按EAGER模式计算权限集。 |
||
EXTEND_OVERLOOK_LAZY |
先查找优先级低组的子组包含本组,再按LAZY模式计算权限集。 |
||
EXTEND_OVERLOOK_INTERSECT |
先查找优先级低组的子组包含本组,再按INTERSECT模式计算权限集。 |
|
多态身份Profile是整个 Zero权限框架中的一个 过度设计 的典范,从实际场景看起来真正使用到这部分的内容仅局限于 对用户而言,一旦登录之后,自己的 Profile 就已经固定,而资源需求要求的Profile则不一定固定,属于变量,最终计算结果近似于查找最短路径,达到用户组这个级别的额外变化模式(包括继承、派生、限制、组合等),最终 Zero权限框架中合计支持64种Profile配置,如此就解决了资源 能不能 访问的问题。 |
N维安全视图(View)
前文解决了资源 能不能 访问的问题,本章就在可访问的基础上解决 访问多少 的问题,Zero权限框架中的 S_RESOURCE 和 S_ACTION 是强绑定关系,它们之间只会单纯对比操作级别和资源需求级别是否可访问,一旦访问成功,就会衍生计算读写操作的边界,在Zero权限框架中读写边界的划定取决于安全视图 S_VIEW 中的定义。
安全视图基础
安全视图的基础维度如下:
| 字段名 | 含义 | 取值 |
|---|---|---|
NAME |
视图名称 |
默认取值 DEFAULT。 |
POSITION |
视图位置 |
默认取值 DEFAULT。 |
OWNER_TYPE |
视图所属者类型 |
只包含两种:ROLE-角色视图,USER-用户视图。 |
OWNER |
视图所属者ID |
如果是角色视图则是角色ID,如果是用户视图则是用户ID。 |
RESOURCE_ID |
视图所属资源 |
当前视图关联的 |
POSITION 和 NAME 构造视图的核心维度,在系统出现不同需求时会起重要作用:
| 场景 | NAME | POSITION |
|---|---|---|
单模块无视图管理 |
DEFAULT |
DEFAULT |
单模块带视图管理 |
? |
DEFAULT |
多模块无视图管理 |
DEFAULT |
? |
多模块多视图管理 |
? |
? |
|
此处解释一下模块的概念,此处的 模块 并非我们开发过程中的模块,此处的模块底层关联模型只有一个,而模块更多是从列表作为入口。例如:
|
而且 POSITION 会比资源多一个维度,通常资源是后接口绑定,如 /api/xxx/search 的资源接口,但这个资源接口由于支持查询引擎语法,可能应用于不同的菜单入口(上述提到的正在执行的订单、已完成的订单)等,这种情况下两个菜单共享了一个资源,而为了针对不同的菜单定义 角色视图/用户视图,最好的方式就是启用 POSITION 参数。如此计算下来,POSITION既不和查询条件绑定(不同页面、不同位置、同一查询条件),也不可以和页面绑定,如果出现 TAB 页签会造成同一个页面中出现两种不同的查询(可能是两种不同的 POSITION),最终它只能和列表的配置绑定,直接在前端中提供它的配置来完成和列表绑定的过程,这一块的用法属于 Zero权限框架中的难点,其应用范围十分广泛,现阶段通常使用场景如下:
-
按类型划分位置信息:分类字段管理
/ambient/tabular/:type不同页面取不同的 POSITION,实现抽象态的列表管理,这种思路同样适用于:档案、合同、项目、员工、客户、分类等。 -
按状态划分位置信息:如待办列表和已办列表,最终访问资源可能都是
/api/todo/search,而由于状态不同,所以设置不同的 POSITION 实现视图的定制。 -
按流程划分QBE:目前系统中流程右上角的QBE列表页是基于此种逻辑,几乎不使用开发的模式就定制完成。
参考下图的结构:
上图结构可以看到 POSITION 和 NAME(在模块访问中通常使用 VIEW)的使用场景会有所差异:
-
POSITION主要用于模块维度的拓展,它的起点是模块。
-
VIEW主要用于视图拥有者维度的拓展,它的起点是拥有者,如角色、用户、目录级(实验版本)。
安全视图类
Zero中存在一个特殊的参数对象:
public class Vis extends JsonObject
// 该参数对象使用时可如下:
@POST
@Path("/{actor}/search")
@Address(Addr.Post.SEARCH)
@Adjust(Orders.MODULE)
JsonObject search(@PathParam("actor") String actor,
@BodyParam JsonObject data,
@QueryParam(KName.MODULE) String module,
@PointParam(KName.VIEW) Vis view);
该参数的格式比较特殊,通常使用的是 [view,position] 的数据格式,也是此处 @PointParam 注解解析的内容,它可以将上述格式直接解析成视图的两个核心维度( NAME, POSITION ),并将该维度应用于任意支持它的接口。Vis类中存在一个特殊方法smart会对视图数据格式做智能解析,它支持的几种格式如下:
-
Vis类型:如果传入的类型是Vis类型,则直接做引用赋值。
-
JsonObject类型:如果传入的类型是JsonObject类型,则解析格式:
{"position": "xxx","view":"xxx"}。 -
JsonArray类型:如果传入的类型是JsonArray类型,则解析格式:
[view,position]。 -
String类型:如果传入类型是String类型,除了完成URL的
decode流程之外:-
如果String类型是JsonObject格式则做一次强制转换,执行JsonObject类型解析。
-
如果String类型是JsonArray格式则做一次强制转换,执行JsonArray类型解析。
-
否则String类型参数直接作为视图名称看待,而赋
POSITION为默认值DEFAULT。
-
-
默认创建专用默认视图:
view = DEFAULT, position = DEFAULT。
窗口定义
安全视图的窗口定义主要依靠下边几个字段:
| 字段名 | 含义 |
|---|---|
PROJECTION |
JsonArray格式,执行该视图的列过滤,直接过滤掉接口返回数据的列信息。 |
CRITERIA |
JsonObject格式,执行该视图默认的 |
ROWS |
JsonObject格式,针对行数据执行筛选,生成 |
VISITANT |
布尔值,是否启用 虚拟视图(资源访问者)。 |
-
PROJECTION会作用于不同类型的前端组件,通常用于LIST/FORM两种,Zero框架中保存列表的列信息以及表单中针对部分表单执行字段过滤就依赖它来完成,它是后置过滤(实际会从数据库中查询出所有信息进行值提取,现阶段没有明显的性能问题)。 -
CRITERIA主要针对于查询,它会隐式修改查询引擎的Qr语法,导致前端发送查询条件在安全视图作用下被直接修改,如果用户中出现了多个角色、多个用户组,则按照最终资源需求中定义的 Profile 来完成查询条件的拼合,默认模式下多个角色之间使用OR连接符。 -
ROWS针对特殊资源提取,提供基于主键的直接命中条件,解决异构查询模式下用户无法使用Join的情况,由于表单是单条数据记录,所以一般表单接口无法支持该属性(设置了也没有效果);通常此属性作用于列表:-
DATA:在数据层面,列表处理过程中直接针对条件执行过滤,典型应用为:菜单筛选、字典筛选、分类树筛选。
-
META:元数据层面,处理过程中只读取ROWS中设置过的的信息(特殊模式下界面呈现模式出现ReadOnly时,它的定义位于UI配置中,而不是安全视图层。
-
|
虚拟视图(资源访问者)部分参考下一个章节的详细说明,上述限制中,虽然 |
视图检索流程
看完了上述安全视图的方方面面之后,视图检索流程就变得很简单了(后端会根据访问资源键值生成 session-<METHOD>:<URI>:<POSITION>/<VIEW> 格式的视图缓存键)。下边是用户访问某个资源接口时的详细流程:
-
用户发送请求到某个资源接口如:
/api/xxx/resource。 -
系统检索该资源是否存在用户级的
S_VIEW记录(OWNER_TYPE = USER, OWNER = <USER_ID>),如果存在该记录,则直接提取安全视图记录对资源执行前后(BEFORE/AFTER)计算。-
BEFORE计算:BEFORE计算的核心算法主要在于修改参数,它的性能更高,一般直接作用于 criteria 参数部分实现查询条件的注入流程。
-
AFTER计算:AFTER计算会完整访问数据库,在查询出来的结果集中做运算,虽然性能会有损耗,但在某些场景下(需要全量元数据)是必须要走AFTER的。
-
-
若不存在用户级的
S_VIEW记录,则继续检索是否有角色级的S_VIEW记录,若存在则计算。 -
上述两步都不存在时,忽略安全视图,可访问资源内所有内容。
|
从上述流程可以知道,用户级安全视图优先级比角色高,一般用户级安全视图都是个人视图模式存在,比如某个模块的视图管理,而角色级的视图都是管理员预设,单个用户不可以更改,比如管理员直接针对财务人员以外的角色设置不可访问某些资源的固定列如薪资、账期等。 |
最终 访问多少 的问题就直接被安全视图处理掉了,不同角色不同用户在此框架之下访问同一个接口时返回数据就可能出现不同,那么这样就解决了资源重用并且 访问多少 的问题。
资源访问者(Visitor)
|
多用于抽象层次比较高的 动态建模 领域的权限控制。 |
本章进入Zero权限框架中一个新的领域:虚拟资源/资源访问者,在讲虚拟资源之前先思考:为什么要使用虚拟资源?前文安全视图中的定义不知道读者是否发现一个小问题:静态的——一旦绑定了资源之后,就只能在某个资源中直接设置参数和条件,而这里设置的所有条件以及参数都 不依赖输入,您考虑下边一个场景。
我在后端书写了一个接口:/api/xxx/:type,这个接口在后端的定义接口和 S_RESOURCE 记录仅有一条(为什么,除非您直接愿意写成 /api/xxx/type1 和 /api/xxx/type2 两个接口),这种场景下,意味着如果 type 有三个值,那么我所期望的安全视图应该有三种,根据前文提到的,您可以设置 S_VIEW 的 POSITION 来限定资源视图,就完成了三种模式下的定义。但若现在我的 :type 参数不是三种,可能存在N种或者上百种,如何解决?Zero为了解决这种安全视图方案,提供了 资源访问者 的概念。资源访问者存储于后端配置表 S_VISITANT 中,一个 S_VIEW(严格说是 VISITANT = true 的视图记录)可能包含无数个资源访问者。
|
从上图可以知道在动态建模过程中,路由器 和 防火墙 可能意味着不同模型,而且应用程序会不断添加新的设备类型,产生新的模型,这种模式下使用
|
Zero中按照如下流程配置一个资源访问者:
-
资源本身(
S_RESOURCE)将该资源定义成一个虚拟资源(VIRTUAL= true)。 -
在(
S_RESOURCE)资源字段中定义访问者基本规则:-
设置访问者语法
SEEK_SYNTAX字段。 -
设置访问者配置
SEEK_CONFIG字段。 -
设置访问者组件
SEEK_COMPONENT字段(Java类名)。
-
-
用户发送请求过来时会读取
S_VIEW的视图信息,一旦读取到视图信息后,对VISITANT= true执行校验,校验成功之后执行资源访问者流程。
资源访问者流程可以总结成两个大步骤:
| 步骤 | 说明 |
|---|---|
真实资源查找 |
根据 |
访问者安全视图 |
根据 |
访问者逻辑
资源访问者的内部逻辑流程如下:
-
检查访问视图
S_VIEW是否一个带有访问者的安全视图(VISITANT = true)的定义。 -
根据资源
S_RESOURCE中的访问者定义(seekSyntax / seekConfig / seekComponent)计算真实资源访问规则。 -
扫描资源访问者的 模式(Replace/Extend)和 作用阶段(EAGER/DELAY),并执行访问者操作。
-
读取访问者相关信息,将这些信息和
S_VIEW中的信息合并计算,计算访问者安全窗口。
关于访问者语法结构定义在此多做点说明,在实际场景中,通常资源的抽象模式(访问者维度)都只有一个维度,所以访问者语法也只是针对资源的访问做一个维度层面的拓展,若您的应用中真的出现了 超级接口,访问者维度需要通过多维模式去实现时,您可以自定义扩展:
-
单维度标准化:
seekSyntax定义可完成。 -
多维度标准化:
seekSyntax新版多维访问者定义完成。 -
多维度扩展:
seekConfig/seekComponent自定义完成。
个人不推荐在系统中做过多的 超级接口 ——一个接口完成多种不同的逻辑,这样的方式运维将会造成一定的压力,并且不容易拓展,但在动态建模和动态接口部分,往往会牵涉到模型的变体,变体出现时这种模式的维度是必须存在的,所以就只能依赖扩展配置来完成。
|
多说一句:Zero扩展框架中存在很多 |
访问者语法 存在的目的是查找视图中对应的访问者信息,由于一个视图可能存在多个访问者,所以系统必须保证本次请求的访问者信息——要么只有1个,要么不存在;否则系统会出现二义性问题导致最终无法定位使用哪个资源访问者来处理请求,所以目前Zero权限框架的版本中,访问者主键 S_VISITANT 表中的 SEEK_KEY 字段是唯一的,而访问者语法的最终目的就是计算 SEEK_KEY 生成访问者查询条件。
(执行维度)模式和阶段
资源访问者语法中的模式 mode 信息( S_VISITANT 表中的 MODE 字段):
-
Replace:替换模式,这种模式下,访问者视图会直接覆盖
S_VIEW中的安全窗口规则,也就是说资源访问不再遵循S_VIEW中的安全规则,而直接使用访问者规则。 -
Extend:扩展模式,这种模式下,访问者视图会和
S_VIEW中的安全窗口规则合并计算,形成新的组合好的访问者规则。
它的作用如下:
资源访问者语法中的阶段 phase 信息( S_VISITANT 表中的 PHASE 字段):
-
EAGER:通常当前资源立即生效,一般执行数据读取时会使用 EAGER 阶段(就在当前接口生效)。
-
DELAY:这种阶段通常是读取配置项作用于子资源或其他资源时生效,一般读取元数据和配置数据时使用 DELAY 阶段(DELAY阶段 DataRegion 中的视图模式依旧生效)。
它的作用如下:
|
对于直接开发的接口,基本上EAGER模式就可以满足大量的接口安全控制需求,您不需要开发额外的逻辑就实现了安全视图的控制,但对于比较特殊的基于配置的流程中,通常接口会分为:元数据接口和数据接口两部分,二者相互影响并相互作用,这样的条件下,资源访问者就体现出它的价值了,一个访问者就解决了元数据接口和数据接口的双重安全作用,而不需要依赖每个位置都定义对应的接口。 |
(定义维度)类型/标识/唯一键
访问者记录中虽然保存了 SEEK_KEY,但在不同的业务场景中,它无法做 全局标识,所以根据实际需求,此处设计在后期做过一些基本改动,访问者标识维度如下:
| 字段 | 含义 |
|---|---|
TYPE |
访问者类型,针对资源维度的分类定义,现阶段支持:FORM、LIST、OP、VIEW 四种,服务于 |
IDENTIFIER |
模型标识符,针对模型维度的分类定义,主要提供master模型的基础访问者(限定于所有场景)。 |
SEEK_KEY |
访问者主键,系统可以根据访问者主键查询访问者信息。 |
访问者标识维度从执行和定义两个方向处理:
-
定义维度:
VIEW_ID, TYPE, IDENTIFIER形成唯一键。 -
执行维度:
VIEW_ID, TYPE, SEEK_KEY形成唯一键。
|
举个例子,在读取表单配置时,表单的配置可直接使用:
这样处理之后就实现了表单资源的唯一访问者读取。 |
SEEK_KEY 目前最常用的两种场景如:
-
动态建模过程中,资源访问会牵涉
controlId(表单、列表的组件ID),它的格式通常如下:{ "type": "LIST / FORM / OP", "controlId": "UI_CONTROL表中定义的组件的ID,由于UI_CONTROL中已经定义了模型标识符,所以此处模型标识符则可直接省略。" } -
静态建模过程中,资源访问者会牵涉到其他内容来构造
SEEK_KEY,如:{ "type": "LIST / FORM /OP", "workflow": "工作流名称" }
|
现在版本中流程 |
访问控制
访问者对列表的控制基本和 S_VIEW 同源,主要覆盖如下:
| 字段 | 含义 |
|---|---|
DM_ROW |
等价于视图中的 ROWS 限定规则,前端标记:H - Horizon,水平限定。 |
DM_QR |
等价于视图中的 CRITERIA 限定规则,前端标记:Q - Query,查询限定。 |
DM_COLUMN |
等价于视图中的 PROJECTION 限定规则,前端标记:V - Vertical,垂直限定。 |
访问者优于视图的地方在于它还提供了表单级的控制,主要覆盖如下:
| 字段 | 关键字 | 含义 |
|---|---|---|
ACL_VISIBLE |
可见性 |
可见属性集设置,限定表单中的某些表单字段是否可见。 |
ACL_VIEW |
只读 |
只读属性集设置,可编辑依靠计算: |
*ACL_VARIETY |
多样性 |
多样性属性集设置,用于控制集合类型的属性变体,如数组类的多样性属性集,递归三种属性集,标记某个子属性配置。 |
ACL_VOW |
引用 |
针对引用类型的属性进行限定,比如订单中关联了员工信息,而员工信息可直接被订单接口的访问者执行表单级属性过滤。 |
ACL_VERGE |
依赖 |
保存了所有依赖属性的相关信息,和引用方向相反。 |
|
上述属性中 ACL_VARIETY 属于特殊访问属性,通常可针对列表、集合等不同的访问属性进行内部数据结构的限定,有了这些限定之后,不同的用户在访问同一张表单可达到如下权限控制效果:
|
配置步骤
到这里,所有和Zero权限框架相关的内容就已经讲完了,本章节主要讲解配置步骤,开发人员可参考此章节配置上述所有内容。
权限基础配置
权限基础配置在于配置用户是否有权限访问某个资源。
-
根据:
S_USER / S_ROLE / S_GROUP / S_PERMISSION设置账号到权限的路径数据信息。 -
开发自定义接口如:
/api/xxx/order,并且将此资源接口信息录入到S_RESOURCE表和S_ACTION表中,并将S_ACTION记录挂在某个S_PERMISSION权限之下。-
S_RESOURCE中针对Profile进行资源需求配置:MODE_ROLE / MODE_GROUP / MODE_TREE,确保用户最终计算出来的权限集在对应的Profile之中(参考:仅配置MODE_ROLE = UNION)。 -
S_ACTION中配置的LEVEL一定要大于S_RESOURCE中定义的操作级别。
-
-
将上述配置通过
Loader导入系统中,重启容器生效(后续使用管理端则不需要重启)。
操作级别的内部定义:
| 值 | 符号 | 含义 |
|---|---|---|
0 |
READONLY |
只读级别,最小等级。 |
1 |
READ |
标准读取,通常是单记录读取。 |
2 |
READ_INTEGRATION |
集成读取,通常会开放接口读取。 |
3 |
READ_EXPORT |
从数据库中批量导出时所需。 |
4 |
ADD |
创建和批量创建级别。 |
5 |
ADD_IMPORT |
导入专用级别。 |
6 |
ADD_INTEGRATION |
集成创建,通常会开放接口创建。 |
7 |
ADD_META |
元数据创建接口,创建配置专用。 |
8 |
EDIT_APPROVE |
审批确认更新接口,部分字段更新。 |
9 |
EDIT |
标准编辑,更新和批量更新级别。 |
10 |
EDIT_INTEGRATION |
集成更新,通常开放接口实现更新逻辑。 |
11 |
EDIT_META |
元数据更新接口,更新配置专用。 |
12 |
DELETE |
标准删除,删除和批量删除级别。 |
13 |
DELETE_PURGE |
清除专用,通常用于数据清空。 |
14 |
DELETE_META |
元数据删除专用,删除配置专用。 |
15 |
FULL |
最高操作级别。 |
视图配置
一般静态视图配置都是配置角色级别的视图相关信息,通常不会配置用户级视图。
-
找到权限基础配置中
S_RESOURCE的主键,填充到S_VIEW中的RESOURCE_ID字段中。 -
没有特殊说明
NAME和POSITION都可以直接配置成DEFAULT默认值。 -
在
OWNER_TYPE中配置ROLE,然后找到登录账号对应的角色ID,填充到OWNER中。 -
配置视图的三个核心维度:
ROWS, PROJECTION, CRITERIA。 -
将上述配置的
S_VIEW记录导入到系统中,重启容器生效(后续使用管理端则不需要重启)。
访问者配置
如果牵涉到访问者配置,则遵循如下流程执行:
-
将资源定义
S_RESOURCE中的资源设置成 虚拟资源(VIRTUAL = true)。 -
为虚拟资源配置 访问者语法,两种配置模式:
-
直接配置
SEEK_SYNTAX字段生成标准化的访问者语法模块。 -
配置
SEEK_CONFIG / SEEK_COMPONENT字段生成扩展的访问者语法模块。
-
-
配置好之后再根据所需为资源配置访问者信息:
S_VISITANT表中追加相关记录。
示例/格式
S_VIEW 中的 PROJECTION
S_VIEW 表结构中的 PROJECTION 通常是 JsonArray 结构,如:
[
"key",
"name",
"code",
"createdAt",
"createdBy"
]
上述配置会包含两个含义:
-
在读取资源过程中使用了该视图之后,只会读取 五列 信息。
-
读取的 五列 会按照配置顺序读取,即列表中的列顺序为:
key, name, code, createdAt, createdBy。
S_VIEW 中的 CRITERIA
S_VIEW 表结构中的 CRITERIA 通常是 JsonObject 结构,直接追加查询引擎语法,最终条件和请求条件按 AND 操作符合并。
S_VIEW 中的 ROWS
S_ROWS 表结构中的 ROWS 是一个 JsonObject 结构,会生成 IN 查询条件,如:
{
"name" : [ "zero.desktop", "zero.desktop.my", "zero.desktop.my.todo-pending"],
"code" : [ "A", "B" ]
}
|
您可以将上述配置片段理解成
根据
|
S_RESOURCE 中的 SEEK_SYNTAX
SEEK_SYNTAX 通常格式如下:
{
"phase": "AFTER",
"data": {
"type": "RECORD",
"viewId": "`${viewId}`",
"identifier": "psi.pos"
}
}
上述代码是一个单维度访问者代码,data 节点会包含一个 type 属性,它和 phase 会构成不同的值处理模式,此处若您的数据来自于输入数据,则可使用 "`" 符号执行 JEXL 转换,如上述数据就会根据 type, viewId, identifier 计算访问者的 SEEK_KEY 来提取访问者信息。
|
上述配置中的
|
SEEK_SYNTAX 的多维格式如下:
{
"phase": "AFTER",
"selector": "io.horizon.spi.secure.ConfineKind",
"kind": "type",
"data": {
"ATOM": "`${identifier}/${control}`",
"FLOW": "`${workflow}/${node}/DEFAULT`"
}
}
上述代码段中,访问者调用中会使用 io.horizon.spi.secure.ConfineKind 完成访问者选择,选择过程根据输入参数 type 值执行计算,由于使用了组件,所以执行过程可忽略 data 节点的数据信息,最终:
-
type = ATOM会生成访问者的SEEK_KEY=<模型标识符>/<控件ID>,动态建模可用。 -
type = FLOW会生成访问者的SEEK_KEY=<工作流名称>/<节点名称>/DEFAULT,流程引擎专用。
|
您也可以定义自己的 Confine 接口组件配置
其中此处的
|
S_VISITANT 中的 ACL_
由于访问者表中的 DM_ 字段格式和 S_VIEW 中保持一致,此处不再重复讲解。
-
DM_ROW 对应 ROWS
-
DM_QR 对应 CRITERIA
-
DM_COLUMN 对应 PROJECTION
最后简单讲解下 ACL_ 表单级语法:
| 字段 | 类型 | 格式和含义 |
|---|---|---|
ACL_VERGE |
JsonObject |
一般格式是: |
ACL_VISIBLE |
JsonArray |
直接是 |
ACL_VIEW |
JsonArray |
直接是 |
ACL_VARIETY |
JsonObject |
Json格式,用于构造 AclView/AclMap专用,执行类型为 DATA。 |
ACL_VOW |
JsonObject |
Json格式,用于构造 AclView/AclMap专用,执行类型为 REFERENCE。 |
最后谈谈响应格式中的的 __acl 字段,这是Zero标准数据规范中的一部分,如果某个接口(包括JsonArray返回的接口)中包含了 __acl 则证明该接口包含ACL控制信息,它的格式如下:
{
"access": [],
"edition": [],
"fields": []
}
上述格式是初期版本格式,代表:
-
fields:当前表单、列表能访问的所有属性集合。
-
access:您可以访问的所有属性集合。
-
edition:您可以编辑的所有属性集合。
认证流程
基本流程
Zero中的登录认证流程标准模式基于 OAuth 认证授权流,它的完整流程图如下:
认证流程主要走三步:
-
使用账号和密码(
MD5加密)访问标准登录接口/oauth/login,拿到当前账号的clientSecret密钥。 -
使用账号ID和密钥访问临时授权码申请接口
/oauth/authorize,获取30s期限的临时授权码code。 -
使用临时授权码
code访问令牌接口/oauth/token,获取资源访问令牌。 -
将访问令牌放到
Authorization请求头中获取资源信息,格式为Bearer <access_token>。
|
Zero权限框架基于 |
接口规范
本章讲解每个接口的相关细节,让您对接口细节有所了解,Zero框架做集成时这是必须步骤,基本的集成接口规范可参考 数据规范 章节的定义。
/oauth/login
| 项 | 值 |
|---|---|
HTTP方法 |
|
URI路径 |
|
请求头 |
|
请求
{
"username":"(账号)",
"password":"(MD5加密过的密码)"
}
| 参数名 | 说明 |
|---|---|
|
用户账号。 |
|
账号密码(MD5加密并且转大写)。 |
响应
{
"data": {
"key": "返回的用户主键",
"scope": "应用名称",
"clientSecret": "用户专用密钥",
"grantType": "authorization_code"
}
}
| 参数名 | 说明 |
|---|---|
|
当前登录账号的 clientId。 |
|
当前应用范围,链接到认证授权中的 |
|
当前账号创建时生成的 |
|
当前OAuth认证的方式,默认使用 |
/oauth/authorize
| 项 | 值 |
|---|---|
HTTP方法 |
|
URI路径 |
|
请求头 |
|
请求
{
"client_id":"用户主键",
"client_secret":"创建账号时生成的64位随机字符串,盐",
"response_type":"...(保留)",
"scope":"vie.app.ox"
}
| 参数名 | 说明 |
|---|---|
|
必须和上一步的值一致,应用范围。 |
|
上一步返回的 |
|
上一步返回的 |
|
上一步返回的 |
响应
{
"data": {
"code": "Gr0RzNkZ"
}
}
| 参数名 | 说明 |
|---|---|
|
临时授权码,随机8位数,并且只有30秒的使用时限。 |
/oauth/token
| 项 | 值 |
|---|---|
HTTP方法 |
|
URI路径 |
|
请求头 |
|
请求
{
"client_id":"用户的ID",
"code":"Gr0RzNkZ"
}
| 参数名 | 说明 |
|---|---|
|
|
|
|
响应
{
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9....",
"refresh_token": "eyJ0.....",
"iat": 1578237840003
}
}
| 参数名 | 说明 |
|---|---|
|
资源访问使用的 JWT 令牌。 |
|
资源访问令牌过期时需要重新生成令牌专用的刷新令牌。 |
|
令牌过期时间 |
异常代码表
本章节末尾附上登录接口常见错误代码表
| 状态码 | 错误码 | 含义 |
|---|---|---|
500 |
-60007 |
服务器内部错误 |
400 |
-60004 |
请求格式不对,必须是Json对象格式 |
449 |
-80203 |
账号不存在 |
401 |
-80204 |
密码错误 |
401 |
-80202 |
scope参数丢失,clientId 和 clientSecret不匹配,无法生成 code |
401 |
-80201 |
授权码code过期,无法生成 token |
423 |
-80220 |
账号被锁定无法使用,联系管理员解锁 |
401 |
-80221 |
输入密码次数超过了系统限制,账号被临时锁定5分钟 |
401 |
-80222 |
验证码输入错误,重新输入 |
403 |
-80212 |
账号没有设置完成,无权登录当前应用 |
403 |
-80213 |
您对请求资源没有操作权限,访问终止,联系管理员授权 |
授权流程
|
前一章节提到了标准的登录流程,本章开始讲解 Zero Extension框架中的授权流程,授权流程基于第一章的消费端,同时还会包含和安全缓存相关的内容,本章意在将整个底层数据和原理级内容给您讲清楚,如此您就可以根据实际所需拓展开发相关功能了。 |
账号登录初始化
前文提到的三个接口调用,只能证明当前账号通过了系统的安全认证(401流程),登录成功之后,系统将会为登录账号初始化所有登录信息,本章详解初始化账号的系统级流程。
|
正常来说,Vert.x的Web应用中 |
令牌生成流程
生成令牌过程中,根据 消费端 提到的数据结构,系统会为每个登录账号生成如下数据:
{
"user": "xxx",
"habitus": "xxx",
"session": "xxx",
"role": [],
"group": []
}
| 属性 | 含义 |
|---|---|
user |
用户主键(登录过程中使用的 |
habitus |
128位随机字符串,为了解决 401/403 过程中系统为会话账号重新生成 session 问题而量身打造的业务级 session key |
session |
从 Vert.x 中读取的会话ID,数据来自 |
role |
数组结构、和当前账号绑定的角色信息 |
group |
数组结构,且在 |
获取了登录账号的上述信息之后,系统会提取 user, session, habitus 三个属性生成访问令牌 access_token(即访问令牌中只包含这三个属性信息),再根据访问令牌生成刷新令牌 refresh_token,以及根据配置计算出令牌过期时间,最终将访问令牌持久化到数据库中,持久化的目的是保证用户在异常管理浏览器或其他非正规操作时,依旧有一个时限内可使用的访问令牌。
登录会话初始化
令牌生成完成后,系统将会调用 ScUser 类中的静态方法初始化 用户登录缓存,ScUser类是Zero为账号封装过的核心用户类,此处不讲解该类的一些成员方法(如视图、Profile访问、用户主键提取),但下边几个高频方法可以给开发带来很大的福利。
| 方法签名 | 返回值 | 含义 |
|---|---|---|
|
|
读取当前登录用户主键 |
|
|
设置当前登录用户主键 |
|
|
将传入 habitus 会话的账号从系统注销 |
|
|
登录初始化,本章节的核心调用函数 |
|
|
使用 habitus 会话ID从环境中获取登录用户信息。 |
|
|
使用 Vert.x 中的安全对象 User 从环境中获取登录用户信息。 |
|
从安全性考虑,开发人员如果只有用户主键是无法从环境中拿到 ScUser 信息的,必须采用框架内置的方式从环境中获取 User 对象(User对象是被 Vert.x 安全框架托管的),使用该对象可直接获取到 ScUser 相关信息。 |
资源请求流程
用户发送请求时,直接将登录过程中拿到的 <access_token> 追加到资源访问的请求头中发送本次请求:
Authorization: Bearer <access_token>
关于缓存池
整个资源访问过程中会使用到两个缓存池用于优化认证授权性能,缓存本身带有 间接会话校验 以证明发送请求者的身份信息:
令牌组合
用户最开始登录拿到的 <access_token> 是访问令牌,其中数据包括:
{
"user": "xxx",
"session": "xxx",
"habitus": "xxx"
}
在认证通过之后,存储到认证缓存池中的数据如下(后续授权流程、安全视图流程、资源访问者流程都会使用到令牌数据):
{
"user": "xxx",
"session": "xxx",
"habitus": "xxx",
"access_token": "xxx",
}
|
上边这种变化就是令牌组合干的事,组合令牌的目的是在特定插件、组件、签名等后端模块中实现快速比对,JWT Token的 |
授权完整流程
前文中讲解了 资源请求流程 的完整步骤,由于认证流程相对简单(会话校验、令牌校验),而授权流程主要会包含三个核心步骤:
-
基本授权:用户是否有权访问该资源。
-
安全视图:当前资源是否启用了安全视图。
-
访问者:启用了安全视图的场景中是否执行访问者流程。
本章节主要针对基本授权流程细节进行讲解(其中 基本授权 属于资源请求流程的后半部分内容)。
标准化输入
认证流程通过后,授权最初会拿到如下输入数据信息(该数据同时也是调用 user.principal() 的结果):
{
"user": "xxx",
"session": "xxx",
"habitus": "xxx",
"access_token": "xxx",
}
该数据信息会在Zero安全框架内部执行标准化,标准化结果为:
{
"user": "xxx",
"session": "xxx",
"habitus": "xxx",
"access_token": "xxx",
"metadata": {
"method": "xxx",
"uri": "xxx",
"requestUri": "xxx",
"view": {
"view": "xxx",
"position"; "xxx"
}
},
"headers": {
}
}
简单说明几点:
-
标准化过程追加了本次请求信息,使用该请求信息可直接提取
S_ACTION,而S_ACTION和S_RESOURCE是一对一绑定,等价于可直接提取资源信息。 -
视图存储采用了
Vis对象(专用视图对象),包含了view和position两个维度。 -
headers中主要捕捉自定义请求头,类似X-Sigma,X-Lang等这种。
|
还需要说明的一点就是注意
两种不同的URI在不同的场景会分别参与不同后续流程运算。 |
基本授权步骤
-
使用标准化输入处理之后的数据构造
ScResource对象(和ScUser类似是对资源结构的封装)。 -
从
ScResource中计算提取资源池中的资源键(resource-<method>:<uri>),使用该资源键访问 资源池。-
如果资源池存在直接走安全视图流程。
-
资源池中不存在则走步骤三。
-
-
根据请求数据(
uri和method)从安全操作中提取S_ACTION记录锁定本次请求的安全操作。 -
根据安全操作提取绑定的资源记录,若资源所需操作等级大于了安全操作等级,证明资源出现了状况(比如升级、迁移等),安全操作失效。
-
操作检查结束后,根据提取的所有信息回写资源池,并完成和资源池的同步。
视图流程
资源同步完成后,就开始根据资源读取当前登录用户的视图信息,视图信息读取分两步:
-
用户视图(高优先级),如果存在用户视图则直接忽略角色视图。
-
角色视图,用户未做任何视图管理时资源的默认视图。
视图读取在消费端章节已经多次提及,此处就不赘述,用户视图实际是挂在用户登录的 habitus 缓存之下,视图键的计算如:
session-<method>:<uri>:<position>/<view>
|
视图在初始化时实际是针对资源池的 双读双写 计算,一方面确保资源池中的资源数据已经是经过了同步的数据,另外一方面保证当前视图绑定的资源信息具有强一致性。
|
资源访问者流程在数据域后期执行,下一章节将会详细介绍。前期执行该流程会导致访问者语法只能根据输入处理,无法根据存储数据处理导致访问者功能缺失,无法处理二阶段读写专用场景。
数据域 / 访问者
数据域功能在Zero框架中是使用 插件 方式启用的,若不启用数据域功能,那么视图功能将不复存在,所以插件位于 zero-rbac 项目中,属于Zero Extension框架部分的内容。数据域功能的开启一般在项目配置文件:vertx-dock.yml 中,配置片段如下:
# 「扩展组件」-----------------------------------------------------------------------
extension:
region:
component: io.vertx.mod.rbac.extension.DataRegion
config:
prefix: /api/
截断检查
|
数据域的功能要根据实际需求来指定,在Zero框架中它属于一个类似 |
配置检查
-
正如上述配置提到,启用DataRegion的第一个条件就是配置了
/prefix。 -
其次HttpMethod方法必须是下边几个值:
-
GET
-
POST
-
OPTIONS
-
-
最后系统会检查请求路径是否以配置路径开始,如上述配置中像
/oauth/login, /oauth/authorize, /oauth/token都不满足以/api/为前缀。
输入检查
数据域是和安全视图配合操作的,若前边资源请求流程中无法读取到视图信息,那么数据域功能就会直接跳过不执行。输入检查是基于 DataBound 对象转换的JSON对象(输入 matrix):
boolean isRegion(final JsonObject matrix)
它的结构可参考 用户登录缓存,参考下边的输入检查表格,按顺序检查:
| 规则 | 结果 |
|---|---|
matrix为空 |
禁用数据域 |
matrix中projection有值 |
启用数据域 |
matrix中credit有值 |
启用数据域 |
matrix中rows有值 |
启用数据域 |
matrix中criteria有值 |
启用数据域 |
matrix中seeker有值 |
根据视图数据的存在与否启用和禁用数据域,无视图数据禁用 |
其他 |
上述规则都不满足,直接禁用数据域 |
Before流程
Before流程主要做三件事:
-
访问者流程:若启用了访问者流程则重新提取视图资源信息修改
matrix。 -
projection修改(先知):根据最终视图数据修改projection。
-
criteria修改(先知):根据最终视图数据修改criteria。
完整流程图如下:
After流程
After流程主要做四件事:
-
访问者流程:若启用了访问者流程则重新提取视图资源信息修改
matrix。 -
record过滤:处理单数据记录的过滤信息。
-
rows过滤:针对集合数据集执行行筛选。
-
collection过滤:针对集合执行列过滤。
响应特殊数据
数据域经过了上述流程之后会对响应数据执行最终更改,在响应数据中会追加如下属性执行特殊操作:
| 属性 | 含义 |
|---|---|
|
基于查询语法的ACL控制,主要针对表单记录操作的ACL响应限制。 |
|
基于查询语法的视图控制,返回 |
管理端
设计原则
前文讨论了Zero权限框架中的 消费端,本章节开始讨论权限框架的 管理端,管理端的主要目的是为您定制所需要的核心配置数据,并且通过交互式界面直接为用户执行权限、视图、访问者的授权工作,管理端入口在Zero Extension框架中有三个:
-
角色管理 → 权限设置
-
权限设置 → 角色授权 → 选择某个角色
-
权限设置 → 用户特权(现阶段版本还未放出来)
管理端的主体是 角色,所以管理端的所有操作都是角色级的,只是在设计过程中管理端遵循如下设计原则:
-
管理端界面使用
S_PACKET / S_PATH进行配置,由于所有管理端都只作用于权限、视图、访问者,所以管理模式可以统一。 -
管理界面分 维度管理 和 配置管理,维度管理通常用于定义:按什么方式管理,而配置管理则着眼于管理什么。
-
管理配置可直接扩展,对整个权限管理部分而言,多一种管理界面只是多了一条
S_PACKET数据配置记录(前端可直接解析实现完整管理模式)。 -
现阶段开放的仅:菜单管理 和 流程管理 两部分内容。
|
由于管理端的配置相对更加复杂,所以本文以现阶段已经配置好的 菜单管理 和 流程管理 为例讲解管理端的配置细节。 |
区域基础
区域(旧版本称为:资源路径)位于 S_PATH 表结构中,它定义了目前权限管理的区域板块,如下图:
在权限管理中,区域会衍生横向的页签,每一个页签由一个区域配置来实现,只是该区域配置中可以包含子区域。权限管理 端的主要目的是针对 S_VIEW 和 S_VISITANT 中的数据执行角色级和用户级的增删改,所以 S_PATH 中的定义相对比较抽象。
-
非区域模式:在非区域模式下
S_PATH中的PARENT_ID通常是NULL值,此时的管理分为两种:-
带维度定义的管理:按照某种维度分类之后执行管理。
-
不带维度定义的管理:直接平行管理,最简单的操作。
-
-
区域模式:在区域模式下
S_PATH通常是一个树型结构,顶层区域(页签)这一级的PARENT_ID通常是NULL值,但子区域是挂在主区域上的。
|
非区域模式适合管理1个资源的场景,如菜单管理,仅管理菜单读取资源的信息就可以完成。区域模式则适合管理N个资源的场景,如流程管理,除了管理流程的OP资源以外,还有流程最右上角的QBE的管理。最终遵循的原则是:所有的子区域负责资源管理的执行,非子区域主要负责区域的调度,非区域模式其实就是特殊区域管理(子区域和父区域)的等价模式。 此处使用 区域 的目的是接口最终会带有 authority region 关键字。 |
维度定义
维度定义在Zero权限管理中是一个比较新的概念,它定义了不同场景的管理行为:
-
比如管理菜单:需要依赖菜单分类之后执行管理,而不是将所有菜单统一管理,这种模式下需定义菜单的 分类维度。
-
比如流程管理:需要依赖选择某个流程之后执行相关管理(包括QBE列表、OP操作),这种模式下维度定义需定义流程的 分类维度。
-
比如模型管理:需要依赖选择某个模型之后执行相关管理,这种模式下维度定义需定义模型的 分类维度。
|
归根到底,管理端的主要操作是针对 |
全区域元数据
初始化区域
Zero权限管理中进入界面之后会首先调用 GET /api/authority/region/:type 接口提取所有合法区域信息。
|
注:此处虽然仅提供了参数
|
该接口的响应格式是一个JsonArray,其中菜单管理区域响应格式如下:
{
"key": "f37f66c0-40a9-4816-ade2-4230f4ee045f",
"name": "菜单设置",
"code": "rule.menu",
"phase": "EAGER",
"runType": "ROLE",
"dmType": "FLAT",
"dmComponent": "io.vertx.mod.rbac.ruler.HSDimNorm",
"dmConfig": {
"...": "(略)"
},
"ui": "HxSite",
"uiSort": 1,
"uiType": "DAO",
"uiConfig": {
"...": "(略)"
},
"uiCondition": {
"...": "(略)"
},
"uiComponent": "io.vertx.mod.rbac.ruler.HSUiNorm",
"uiSurface": {
"...": "(略)"
},
"sigma": "Qxw5HDkluJFnAPmcQCtu9uhGdXEiGNtP",
"language": "cn",
"active": true,
"createdBy": "zero-environment",
"label": "菜单设置"
}
上述结构的构造来自于 S_PATH 中的配置,其中只有一个属性比较特殊,ui 属性来自于配置 uiSurface 中的 webComponent 属性,此处对照 S_PATH 对各个属性再加以说明(此处只枚举特殊属性,不枚举Zero规范下的标准化字段如 sigma, active 等)。
| 属性 | 子属性(Json格式内定义) | 数据列 | 含义 |
|---|---|---|---|
name |
NAME |
菜单权限管理标题 |
|
label |
来源于 name |
||
code |
CODE |
菜单权限管理的系统编码,该系统编码会辅助后期生成配置数据和取值专用数据。 |
|
phase |
PHASE |
UI数据的提取阶段。 |
|
runType |
RUN_TYPE |
当前配置管理类型,目前只支持:ROLE(角色级)。 |
|
mapping |
MAPPING |
从 dm → ui 的转换,用户触发维度操作时,将维度数据转换成界面数据的查询条件专用映射配置。 |
|
dmType |
DM_TYPE |
维度分组类型。 |
|
dmComponent |
DM_COMPONENT |
维度数据提取专用组件。 |
|
dmCondition |
DM_CONDITION |
维度数据提取时的查询条件,遵循查询引擎专用语法,会被 dmComponent 消费。 |
|
dmConfig |
items |
DM_CONFIG |
(前端)维度界面配置 / items用于描述维度数据的呈现基础规则。 |
webAction |
(前端)维度界面配置 / webAction负责维度数据操作触发的连接(右上角按钮事件配置)。 |
||
ui |
来源于 uiSurface 中的 webComponent 属性,当前管理界面的主要组件。 |
||
uiSort |
UI_SORT |
当前区域的排序,最终在页签中呈现的顺序按 uiSort 进行排列。 |
|
uiType |
UI_TYPE |
界面数据提取类型。 |
|
uiConfig |
UI_CONFIG |
界面数据配置专用,会直接被 uiComponent 在后端消费。 |
|
uiComponent |
UI_COMPONENT |
界面数据配置组件,用于提取界面数据以及处理专用。 |
|
uiCondition |
UI_CONDITION |
界面数据查询模板,遵循 QR 查询引擎专用语法。 |
|
uiSurface |
webComponent |
UI_SURFACE |
(前端)界面配置数据 / webComponent,界面呈现主要组件。 |
webBind |
(前端)界面配置数据 / 前端和资源绑定的基础规则,某个组件和资源编码绑定,不同组件处理不同资源。 |
||
webTree |
(前端)界面配置数据 / 界面数据以树模型呈现时,提供树型组件参数。 |
||
webData |
(前端)界面配置数据 / 界面数据流规则定义。 |
维度属性 DM / 界面属性 UI
Zero权限管理框架中有两个核心前缀:
-
DM:维度配置前缀,用于配置当前管理界面所需的维度信息(按某种维度分类管理规则),且不同前端组件对维度定义有所区别。
-
UI:界面配置前缀,用于配置当前管理界面交互式组件专用界面信息。
|
最初您可能对 DM 以及 UI 的概念不太理解,但一旦配置过一个完整的权限管理模块(如菜单管理、流程管理),您就逐步理解二者的区别和用法了,权限管理界面大部分内容是通过配置的手段实现,并非使用代码开发的方式,所以才会有复杂的 DM / UI 架构来辅助您生成不同的界面。 |
特殊属性说明
-
阶段属性
phase:阶段属性和权限管理其他位置的取值一致:EAGER / LAZY两种:-
EAGER:加载区域元数据时同时提取界面数据资源,如菜单管理中系统所有菜单需要在加载界面时全部提取出来,才可以执行配置,这种模式是EAGER模式。
-
LAZY:加载区域元数据时不提取界面数据,这种一般用于 动态建模,提取维度数据之后,借用维度数据接口执行二次加载,此时界面数据才会被提取出来。
-
-
运行属性
runType:该属性作为查询条件使用,现阶段只支持ROLE一种(角色级管理),后续可支持多种,取值对应到S_VIEW中的核心字段OWNER_TYPE,目前有三种:ROLE / USER / DIRECTORY。 -
映射属性
mapping:映射属性十分重要,考虑这样一个场景:左侧有一个模型分类的树,来自于字典表X_CATEGORY,当界面出现字典树时,用户会点击某个类型的字典去过滤所有模型数据,如:服务器下的路由器管理 等,这种场景下要将字典记录category转换成模型记录的查询条件实现过滤,它们之间表结构通常会如下图,所以映射属性就可以辅助我们完成从 DM 到 UI 的转换(此种转换通常适用于维度数据为动态数据的场景)。 -
维度类型
dmType:定义维度数据的提取模式值 含义 NONE
无维度数据提取,直接平行定义
FLAT
列表型数据提取,只划分区域执行列表型提取操作
TREE
树型数据提取
FOREST
森林型数据提取,等价于 FLAT 和 TREE 两种模式综合
-
界面类型
uiType:定义界面数据的提取模式值 含义 NONE
无数据源,纯前端界面开发模式
WEB
静态数据专用,直接从
json配置文件中解析界面数据完成数据提取DAO
动态数据提取,使用静态模型的Dao层直接提取数据
ATOM
动态数据提取,使用动态模型的数据提取
DEFINE
自定义模式,组件使用模式,这种模式依赖 uiConfig 和 uiComponent 的自定义开发
-
dmConfig中的
webAction属性,该属性主要用于构造连接保存模型,通常会实现防重复加载、事件连接、点击加载等一连串前端事件流程,命名规范如下:-
连接点ID:
__ID作连接点前缀,触发Ux.connectId函数的按钮。 -
被连接点ID:
__CID作被连接点前缀,Ux.connectId函数参数id对应的按钮。
-
-
uiSurface中的
webData属性,该属性用于处理页面数据流,提供了数据的 初始化、读取、变更、提交 等核心数据流程,常用的几个属性如下:参数名 含义和值 paramIn
数据初始化时的默认数据信息。
paramView
视图相关数据,主要设置视图的两个核心维度
view, position,不设置时使用默认值:DEFAULT / DEFAULTparamVisit
初始化访问者
S_VISITANT时的专用数据配置,一般新增访问者需要。paramDefault
无值默认行为,ALL代表全选,NONE代表全部不选中,常用值为ALL。
initializer
初始化JS函数
requester
提交专用JS函数
单区域:元数据
Zero权限管理中在点击页签时(除开第一个页签)会调用 GET /api/authoriy/region-d/:key 接口提取单个区域的所有配置信息,它的响应格式如下:
{
"group": {
"...": "(略)"
},
"config": {
"...": "(略)"
},
"ui": "HxSite",
"key": "f37f66c0-40a9-4816-ade2-4230f4ee045f",
"label": "菜单设置",
"value": "rule.menu",
"datum": {
"...": "(略)"
},
"data": {
"...": "(略)"
}
}
上述结构是对单个区域执行过标准化的结构,其内容和含义如下:
| 节点名称 | 含义 |
|---|---|
group |
维度处理专用数据 |
config |
界面配置专用数据 |
datum |
维度元数据定义,对应前一章节的 region 定义信息 |
data |
界面显示专用数据 |
|
不论是全区域元数据还是单区域元数据中,都牵涉
您可以在Zero UI前端开发过程中拓展自己的权限管理专用组件,所有 |
单区域:数据
Zero权限管理中在点击页签时除了提取元数据信息以外,还会调用 POST /api/authority/region-v/:key 接口提取单个区域的所有数据信息,数据信息响应格式如下:
{
"res.menu.read": {
"data": [
{
"h": {
"name": [...]
},
"q": {},
"v": [],
"view": "DEFAULT",
"position": "DEFAULT",
"virtual": false
}
],
"metadata": {
"h": {
"config": {},
"mapping": {
"key": "name"
}
},
"q": {},
"v": {}
}
}
}
如果读取数据时存在多个资源,此处响应数据不止一个 key 属性,上述示例中的属性键为 res.menu.read,这个值就是资源 GET /api/menus 接口的资源编码(后端系统中 S_RESOURCE 表对应的 CODE 字段的值),菜单在执行过滤过程中,Zero系统中采用的是行筛选(ROWS 模式),所以最终存储在视图中的数据结构如:
{
"name": [
"...."
]
}
您可以从响应数据的 data 节点中捕捉到 h 类数据,而 metadata 中存储的则是转换规则,响应值的基本规则如:
| 属性 | 对应属性 | 含义 |
|---|---|---|
h |
ROWS |
h代表Horizon,行筛选。 |
v |
PROJECTION |
v代表Vertical,列筛选。 |
q |
CRITERIA |
q代表Query,查询引擎筛选。 |
In/Out预处理
参考下边的完整流程图,前边三个服务端接口已经讲解过,本章节主要讲解 In/Out 的数据预处理流程(棕色部分)的代码逻辑:
远程数据读取后,一般的 Hx 组件属性会包含如下核心结构:
| 属性 | 子属性 | 类型 | 含义 |
|---|---|---|---|
$anchors |
无 |
props / JsonArray |
被连接的按钮 |
$initial |
无 |
props / JsonObject |
初始化值专用,一般为 |
$owner |
无 |
props / JsonObject |
当前拥有者信息,角色权限中则是角色信息,用户权限中则是用户信息。 |
$region |
无 |
props / JsonObject |
当前单区域元数据信息(来自 |
$bindValue |
无 |
props / String |
当前组件绑定的资源code,如菜单管理中该值是 res.menu.read。 |
config |
webAction |
props / JsonObject |
当前组件的操作相关信息,同时包含 |
webBind |
props / JsonObject |
组件和资源绑定的基本信息,结构为: |
|
webComponent |
props / String |
当前组件名称(该属性已在外层消费)。 |
|
webData |
props / JsonObject |
组件绑定的数据流配置信息。 |
|
webTree |
props / JsonObject |
(非必须)维度部分设置树型菜单时的特殊配置。 |
|
webUi |
props / JsonObject |
对应后端的UI部分的配置信息。 |
|
data |
无 |
props |
当前组件的数据源信息,数据源会在预处理过程中执行 分组之后拉平 的计算。 |
$keySet |
无 |
state / Set |
当前组件为Checkbox类型时,用户交互时的选中值受控属性。 |
$keyDefault |
无 |
state / Set |
当前组件为Checkbox类型时,初始化进入界面时候的选中值集合。 |
$bindData |
<resource> |
state / JsonObject |
当前组件使用(包括子组件)的所有数据信息。 |
$inited |
h |
state / JsonObject |
当前组件消费的专用 ROWS 值。 |
q |
state / JsonObject |
当前组件消费的专用 CRITERIA 值。 |
|
v |
state / JsonObject |
当前组件消费的专用 PROJECTION 值。 |
|
上述所有结构中,并非所有组件都包含全部结构,尤其是基础数据 data 的结构相对浮动,但大部分结构是一致的,最终可达到所有组件一致性。
|
In预处理流程
In 的预处理流程会调用 aclRegionInit 函数,并构造前一小节提到的状态(state)中的所有数据信息。
aclRegionInit 执行流程如下( 注意aclIn是在哪个步骤被调用 ):
-
先检查和判断 $bindData 的合法性,从前边章节可以知道 $bindData 的数据结构为:
resource = data(h,q,v)的结构,当出现子组件时,该属性会因为继承的关系直接有值,此时就不用从远程调用POST /api/authority/region-v/:key接口提取数据,即:从远程加载单区域的数据是一次性行为,如果是父类组件,会一次性从远程加载所有绑定资源对应的数据信息(包括 S_VIEW 和 S_VISITANT)。 -
根据 config 中的两个属性
webBind / webComponent计算当前组件需要使用的资源数据:{ "HxSite": "res.menu.read" }$bindData 的结构是
resource = data(h,q,v)的一个Map结构,而webComponent就是当前消费的组件名,最终计算的dataKey就是资源编码,提取的自然是资源数据。当前权限管理中暂时不支持在同一个面板中使用不同组件修改同一资源的情况,而且从实际经验看起来这种情况大概率是配置有问题,交互层会导致数据流混乱。后续版本中会根据实际需求拓展成绑定的资源信息不止一个的情况,由于JS是弱类型,此处后续可使用类似如下:
{ "HxSite": [ "res.menu.read", "res.xxxx", "..." ] }这一步完成之后,状态中会出现
$inited变量(初始化数据变量),其中包括:h, q, v, virtual, view, position等。 -
(AOP)执行
keyFn2二阶函数重新处理$keySet,该步骤通常是 过滤、转换、计算 等。 -
调用
aclIn函数执行标准化计算$keySet,并根据$keySet计算$keyDefault集合。
上述流程中最后一步就会执行 aclIn 函数,它会直接调用 webData 中配置的 initializer,此处需要您关注的是 initializer 的第二参数 param 的数据结构:
| 属性 | 含义 |
|---|---|
webValue |
(数据)直接绑定前边步骤计算出的 |
webData |
(配置)从 config 中提取配置 |
webFn |
(行为)直接绑定 aclRegionInit 最外层传入的第二参,该参数是一个JsonObject类型,但包含了类似 |
Out预处理流程
Out 预处理流程是在最终点击 保存 按钮之前触发,主要目的是生成最终接口所需的数据信息,该流程的输入就是 $keySet。
|
Zero前端组件中有一个不成文的规定,就是通常使用多选操作时,会生成 |
Out 预处理流程会直接调用 aclOut 函数,它会直接调用 webData 中配置的 requester,此处需要您关注的依旧是 requester 的第二参数 param 的数据结构:
| 属性 | 含义 |
|---|---|
webValue |
(数据)直接绑定前边步骤计算的 |
webSelected |
用户操作结果。 |
webData |
(配置)从 config 中提取配置 |
webOption |
(外层配置)绑定外层传入的选项信息。 |
详解initializer
从示例配置中可以知道,菜单中的 initializer 配置的值为 IN_H,此处先列出目前版本拥有的所有 initializer 函数:
| 函数名 | 标记 | 含义 |
|---|---|---|
IN_H |
H-视图 |
提取视图中的 rows 数据。 |
IN_HV |
H-视图,V-访问者 |
提取视图访问者中的 dmRow 数据。 |
IN_HV_BY |
H-视图,V-访问者 |
带条件提取视图访问者中的 dmRow。 |
此处先看看菜单级的 initializer 具体做了什么事(解析 IN_H ),菜单的元数据结构如下( props → data ):
{
"SIDE-MENU": [],
"TOP-MENU": [],
"NAV-MENU": [],
"...": "(略)"
}
|
|
-
initializer 首先构造数据源(本例中是所有可选中菜单记录),数据源最终返回必须是一个 JsonArray 的数组结构(最少在目前的权限管理中如此),它的计算基础就是属性 props 中的 data 数据节点:
-
如果 data 是 JsonArray 类型,则直接将它返回成可操作的数据源节点。
-
如果 data 是 JsonObject 类型,在当前版本中只会出现一种情况(如菜单管理),分组数组类型,如示例代码段,此时数据源会执行
Ux.elementFlap动作将所有的value连接到一起形成一个组合的新数组。
-
-
根据标识
v, q, h提取值中定义的mapping映射信息,参考前文的预处理参数,提取路径为:webValue → metadata → ? → mapping,其中?表示传入的标识,在IN_H类型函数中,标识当然是h,如果没有在后端配置任何相关的mapping性质,则会生成下边代码段。{ "key": "key" } -
根据数据源和映射配置(前两步结果)生成最终的数据集,并执行
$keySet / $keyDefault的标准化流程。 -
设置默认选中行为(
paramDefault配置),一般默认选中用于解决:无数据模式 究竟初始化时没有数据是全部选中还是全部不选。
详解requester
requester 在流程图中和 initializer 是两个相反的操作,此处列出目前版本所有存在的 requester 函数:
| 函数名 | 标记 | 含义 |
|---|---|---|
OUT_C |
C-子组件 |
根据子组件配置生成子组件单独的视图数据提交数据信息。 |
OUT_H |
H-视图 |
根据 |
requester 的代码流程这里就不列举,大部分 OUT 类的执行流程都有所差异,不论哪种流程,最终都会构造数据发送到接口:POST /api/authority/region/:value 中,它的请求格式通常如下:
{
value: "rule.menu",
$body: {
"owner": "xxx",
"ownerType": "ROLE",
"resource": {
"res.menu.read": {
"criteria": {},
"projection": [],
"rows: {},
"view": "DEFAULT",
"projection": "DEFAULT",
"uid": "xxx",
"visitant": [
{
"identifier": "xxx",
"mode": "xxx",
"phase": "xxx",
"seekKey": "xxx",
"type": "xxx",
"dmRow": {}
}
]
}
}
}
}
上述请求格式是标准化格式:
-
value:对应到S_PATH表中的CODE,管理端区域编码,每次保存数据会直接更新该区域下所有关联资源的视图以及访问者信息。 -
owner/ownerType:用于定位单个角色或用户相关信息。 -
resource:该节点为核心数据节点,通常格式为resourceCode = data的结构,此处JsonObject键是资源编码,可标识唯一资源信息。 -
resource对应数据节点:数据节点中第一层是 单视图 相关信息,包含了视图相关信息,两个特殊节点如下:-
uid:该节点为加密专用节点,前后端会对照计算以确定当前权限更新信息是合法的。 -
visitant:用于设置当前更新视图相关的访问者信息,访问者信息会同步到后端。
-
|
该接口会执行 添加/保存 两个核心动作,若视图或访问者不存在,只要数据结构合法,最终都会同步到后端数据库中。目前的权限管理版本还不牵涉一个资源多个视图的更新,从实际使用过程可以知道,一个资源多个视图的更新基本不会存在于权限管理流程,这种场景在现阶段没有需求,所以资源关联数据格式依旧是 JsonObject 格式。 |
父子嵌套调用
父子调用过程主要存在于流程管理中,流程管理的基本布局如下:
上述结构图中,出现了父子级区域,所以本章主要讲解父子级区域的核心流程,父子级区域同样会走 initializer 和 requester 的基础流程,中间部分的内容是不会变化的,但有几个点需要注意:
-
子组件执行
requester的时间并非是提交前,而是触发Checkbox时,当子组件触发了Checkbox中的onChange函数,子类的requester流程就直接启动,最终会生成完整的请求结构,并且将结构中的resource回传到父类回调函数中。 -
父组件状态
state中$keyChild的完整结构就是resource = data,但和不带父子结构的内容有所差异的地方在于,此处的resource包含了当前父区域下所有子区域关联的资源,一般和webBind中定义的资源保持一致。 -
OUT_C表面上看起来是一个requester函数,实际是一个组合函数,它负责将所有合法的$keyChild中存在的资源集体打包,最终构造发送到后端接口中的标准化请求数据。
|
理解清楚了 |
示例/格式
S_PATH中的DM_CONFIG
{
"items": {
"root": "BAG-MENU",
"exclude": [
"zero.desktop"
],
"children": [
"SC-MENU,服务目录菜单",
{
"key": "NAV-MENU",
"title": "主页快捷菜单",
"value": [
"NAV-MENU",
"REPO-MENU"
]
},
"SIDE-MENU,应用主菜单"
]
},
"webAction": {
"icon": "upload",
"text": "保存",
"type": "primary",
"key": "__ID_MENU_SAVE",
"connectId": "__CID_MENU_SAVE"
}
}
上述配置中的 items 节点是 HxSite 专用的配置信息,此处就不介绍相关内容,而 webAction 定义了页面专用按钮:连接点和被连接点的按钮配置相关信息。另外一种 items 的配置如下:
"items": {
"dao": "cn.vertxup.workflow.domain.tables.daos.WFlowDao"
}
这种情况下需要结合 DM_CONDITION 中的查询条件来实现维度数据的拉取工作。
S_PATH中的DM_MAPPING
{
"name": "label",
"code": "key",
"definitionKey": "value",
"type": "type"
}
S_PATH中的UI_CONDITION
{
"sigma": "`${sigma}`",
"appId": "`${appId}`",
"": true
}
S_PATH中的UI_CONFIG
{
"dao": "cn.vertxup.ambient.domain.tables.daos.XMenuDao",
"output": {
"group": "type"
}
}
此处的 output 可以包含不同的模式,现阶段主要支持两种:
-
直接以 JsonArray 的方式返回数据到前端。
-
以按照某个维度字段分组的方式将原始 JsonArray 计算生成一个 JsonObject,最终返回到前端。(
initializer和requester会在前端自动计算数据源,所以不用过于担心结构问题。)
S_PATH中的UI_SURFACE
{
"webComponent": "HxSite",
"webBind": {
"HxSite": "res.menu.read"
},
"webTree": {
"parent": "parentId",
"title": "text",
"text": "text",
"value": "name",
"sort": "order"
},
"webData": {
"paramView": {
"view": "DEFAULT",
"position": "DEFAULT"
},
"paramDefault": "ALL",
"initializer": "IN_H",
"requester": "OUT_H"
}
}
带父子级的 UI_SURFACE 参考如下(父类):
{
"webComponent": "HxFlow",
"webChildren": [
{
"component": "HxQueue",
"key": "rule.flow.queue"
},
{
"component": "HxAction",
"key": "rule.flow.action"
}
],
"webBind": {
"HxQueue": "res.views.fetch",
"HxAction": "res.op.by.control"
},
"webWelcome": {
"header": {
"label": "选择流程",
"value": "流程编码"
},
"alert": {
"message": "流程权限说明",
"description": [
"您可以在当前页面设置不同角色针对不同流程的相关权限。",
"`保存`操作只会保存当前选择流程的权限配置。",
"`保存所有`操作会一次性保存当前页面所有设置过的流程权限配置。",
"视图权限用于设置该角色在不同列表可访问的视图可见性以及视图的特殊定制。",
"操作权限用于设置该角色在不同流程节点可执行的按钮集合以及特殊定制。"
]
}
},
"webData": {
"requester": "OUT_C"
}
}
|
一般如果带有父组件,组件本身的触发会在父类实现,而父类的 |
带资源访问者的子类使用的 UI_SURFACE 配置:
{
"webNode": {
"event": "节点值",
"name": "流程节点名称",
"action": "操作列表",
"empty": "请选择您要设置权限的流程节点"
},
"webData": {
"paramIn": {
"workflow": "PROP:workflow.value",
"event": "STATE:node.event"
},
"paramView": {
"position": ":workflow",
"view": ":event"
},
"paramVisit": {
"phase": "EAGER",
"mode": "REPLACE",
"identifier": "ui.op",
"type": "OP",
"seekKey": ":workflow/:event/DEFAULT"
},
"paramDefault": "ALL",
"initializer": "IN_HV_BY",
"requester": "OUT_H"
}
}
|
上述配置片段是目前已经存在的片段信息,提供片段仅让您有所参考,如此您就可以彻底理解 管理端 的基本配置了,扩展开发管理端也变得相对容易很多,剩余关于权限在请求流程中的内容后续补充文档来加以说明。 |